﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExtremeLearningMachine;
using System.IO;
using System.ComponentModel;

namespace HarmonnySearch
{
    class Program
    {
        #region Attributes
        static Random Rand = new Random(19);
        static StreamWriter File;
        static DateTime StartTime;
        static DateTime EndTime;
        static string DataSetPath;
        static string SimulationResultPath;
        static bool SavePrediciton = true;
        #endregion
        
        #region ConfigurationParameters
        static double inputA = 0.15;
        static double inputB = 0.85;
        static double outputA = 0.15;
        static double outputB = 0.85;
        static int Iterations = 30;
        static int MemorySize = 10;
        static int MaxHiddenNodes = 200;
        static int MaxIterations = 200;
        static EDataType DataType = EDataType.Predction;
        static bool DoInputSearch = true;
        static EActivationFunctionType ActivationFunction = EActivationFunctionType.SigmoidLogistic;
        static EEvaluationFunctionType EvaluationFunction = EEvaluationFunctionType.Weight;
        static EPerformanceInfo PerformanceInfo = EPerformanceInfo.RMSE |EPerformanceInfo.EMQ | EPerformanceInfo.EPMA;
        #endregion

        static void Main(string[] args)
        {
            #region Paths

            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Ailerons//delta_ailerons.txt";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Ailerons//delta_ailerons_weight.csv";
            
            DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Caxias//caxias.txt";
            SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Caxias//caxias_weight.csv";

            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Furnas//furnas.txt";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Furnas//furnas_weight.csv";

            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//California Housing//cal_housing.txt";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//California Housing//cal_housing_Weight.csv";


            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Diabetes//diabetes.txt";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Diabetes//diabetes_simulacao_weight.csv";

            //DataSetPath = "c://users//edgar almeida//desktop//base de dados//Wiscoin Breast Cancer//r_wpbc.data";
            //SimulationResultPath = "c://users//edgar almeida//desktop//base de dados//Wiscoin Breast Cancer//r_wpbc_weight.csv";

            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Itaipu//itaipu.txt";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados///Itaipu//itaipu_weight.csv";

            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Elevators//delta_elevators.txt";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Elevators//delta_elevators_weight.csv";

            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Auto-price//price.txt";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Auto-price//price_weight.csv";

            //DataSetPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Abalone-n//abalone.data";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Abalone-n//abalone_weight.csv";

            //DataSetPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\ComputerActivity\cpu_act.data";
            //SimulationResultPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\ComputerActivity\cpu_act_weight.csv";

            //DataSetPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\Census\house-price-8L\house_8L.data";
            //SimulationResultPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\Census\house-price-8L\house_8L_weight.csv";

            //DataSetPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\triazines\triazines.data";
            //SimulationResultPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\triazines\triazines_weight.csv";

            //DataSetPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\Machine-Cpu\machine.data";
            //SimulationResultPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\Machine-Cpu\machine_weight.csv";
            

            #endregion
            #region 1
            File = new System.IO.StreamWriter(SimulationResultPath, true);

            string info = "Seed; Hiden Nodes number; Selected Inputs; ";

            if (DataType == EDataType.Predction)
                info = string.Concat(info, (PerformanceInfo & EPerformanceInfo.EMQ) != EPerformanceInfo.None ? "EMQ-Train; EMQ-Val; " : string.Empty,
                    (PerformanceInfo & EPerformanceInfo.DEV) != EPerformanceInfo.None ? "DEV-Train; DEV-Val; " : string.Empty,
                    (PerformanceInfo & EPerformanceInfo.RMSE) != EPerformanceInfo.None ? "RMSE-Train; RMSE-Val; " : string.Empty,
                    (PerformanceInfo & EPerformanceInfo.EPMA) != EPerformanceInfo.None ? "EPMA-Train; EPMA-Val; " : string.Empty);
            else if ((PerformanceInfo & EPerformanceInfo.SR) != EPerformanceInfo.None)
                info = string.Concat("SuccessRate-Train; SuccessRate-Val; ");
            
            info = string.Concat(info, "Time");
            info = string.Concat(info, "; StopCount");

            File.WriteLine(info);        
            File.Close();

            Console.WriteLine("Processando arquivo: " + SimulationResultPath);

            for (int i = 1; i <= Iterations; i++)
            {
                Console.WriteLine("Início iteração: " + i);
                int seed = Rand.Next();
                Random _Random = new Random(seed);

                StartTime = DateTime.Now;
            #endregion
                DataProvider prov = new DataProvider(DataSetPath, DataType, _Random);

                if (DataType == EDataType.Predction) prov.NormalizeData(inputA, inputB, outputA, outputB);

                prov.ShuffleDataSet(seed);
                prov.SplitData();
                //prov.SplitData(100, 86);
                //prov.SaveToFile(DataSetPath);

                //bool teste = false;
                //if(teste)
                //{
                //    ELM elm = new ELM(prov, 25, new Random().Next(), ActivationFunction);
                //    elm.Train();
                //    double rmseTrain = elm.GenerateRMSEForTrain();
                //    double epmatrain = elm.GenerateEPMAForTrain();
                //    elm.Validate();
                //    double rmseVal = elm.GenerateRMSEForValidation();
                //    double epmaVal = elm.GenerateEPMAForValidation();
                //}

                HS hs = new HS(seed, MemorySize, MaxHiddenNodes, MaxIterations, prov, ActivationFunction, EvaluationFunction, PerformanceInfo, DoInputSearch);
                object[] returnVal = hs.Run();
                int stoped = (int)returnVal[1];
                HSParticle bestParticle = (HSParticle)returnVal[0];

                EndTime = DateTime.Now;

                string resultString = seed + "; " +
                                      bestParticle.Config.HidenNodesNumber + "; " +
                                      InputsToString(bestParticle.GetSubListValuesFromIndex((int)prov.InputsN)) + "; ";

                if (DataType == EDataType.Predction)
                {
                    if ((PerformanceInfo & EPerformanceInfo.EMQ) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.EMQTrain.ToString("0.######") + "; ";
                        resultString += bestParticle.EMQValidation.ToString("0.######") + "; ";
                    }

                    if ((PerformanceInfo & EPerformanceInfo.DEV) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.DEVTrain.ToString("0.######") + "; ";
                        resultString += bestParticle.DEVValidation.ToString("0.######") + "; ";
                    }

                    if ((PerformanceInfo & EPerformanceInfo.RMSE) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.RMSETrain.ToString("0.######") + "; ";
                        resultString += bestParticle.RMSEValidation.ToString("0.######") + "; ";
                    }

                    if ((PerformanceInfo & EPerformanceInfo.EPMA) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.EPMATrain.ToString("0.######") + "; ";
                        resultString += bestParticle.EPMAValidation.ToString("0.######") + "; ";
                    }
                }
                else if ((PerformanceInfo & EPerformanceInfo.SR) != EPerformanceInfo.None)
                {
                    resultString += bestParticle.SRTrain.ToString("0.######") + "; ";
                    resultString += bestParticle.SRValidation.ToString("0.######") + "; ";
                }

                resultString += EndTime.Subtract(StartTime).ToReadableString();
                resultString += "; " + stoped;

                File = new System.IO.StreamWriter(SimulationResultPath, true);
                File.WriteLine(resultString);
                File.Close();

                ////////////////// Salvando predição ///////////////////////////////
                if (SavePrediciton)
                {
                    prov = new DataProvider(DataSetPath, DataType, Rand);
                    if (DataType == EDataType.Predction) prov.NormalizeData(inputA, inputB, outputA, outputB);

                    int inputNodesLengh = bestParticle.GetFlagCountFromSubListValues((int)prov.InputsN);

                    Data[] dataSet = new Data[prov.DataSetLines];
                    int index = 0;

                    for (int j = 0; j < prov.DataSetLines; j++)
                    {
                        index = 0;
                        dataSet[j] = new Data(new double[inputNodesLengh], new double[prov.OutputsN]);
                        dataSet[j].Output = prov.DataSet[j].Output;

                        for (int k = 0; k < prov.InputsN; k++)
                        {
                            if (bestParticle.Values[k] == 1)
                            {
                                dataSet[j].Input[index] = prov.DataSet[j].Input[k];
                                index++;
                            }
                        }
                    }

                    int hiddenNodes = bestParticle.GetHiddenNodes((int)prov.InputsN, MaxHiddenNodes);

                    DataProvider provForPrediction = new DataProvider(dataSet, prov.DataType);

                    index = 0;
                    for (int k = 0; k < prov.InputsN; k++)
                    {
                        if (bestParticle.Values[k] == 1)
                        {
                            provForPrediction.MaxValue[index] = prov.MaxValue[k];
                            provForPrediction.MinValue[index] = prov.MinValue[k];
                            index++;
                        }
                    }

                    for (int l = 0; l < prov.OutputsN; l++)
                    {
                        provForPrediction.MaxValue[index] = prov.MaxValue[l + prov.InputsN];
                        provForPrediction.MinValue[index] = prov.MinValue[l + prov.InputsN];
                        index++;
                    }

                    ELM elm = new ELM(provForPrediction, hiddenNodes, seed, ActivationFunction, bestParticle.ELM.GetB, bestParticle.ELM.GetW);

                    double real = 0;
                    double prediction = 0;
                    double Error = 0;

                    File = new System.IO.StreamWriter(SimulationResultPath + "predicao.csv" + i, true);

                    for (int m = 0; m < provForPrediction.DataSetLines; m++)
                    {
                        for (int j = 0; j < provForPrediction.OutputsN; j++)
                        {
                            real = provForPrediction.DeNormalizeData(provForPrediction.DataSet[m].Output[j], outputA, outputB, j + inputNodesLengh);
                            prediction = provForPrediction.DeNormalizeData(elm.GetT[m][j], outputA, outputB, j + inputNodesLengh);

                            Error = real - prediction;
                            File.WriteLine(real + ";" + prediction + ";" + Error);
                        }
                    }
                    File.Close();
                }
                //////////////////////////////////////
                Console.WriteLine("Fim iteração: " + i);
            }
            Console.WriteLine("Fim simulação");



            ////////////////////////////////////////////////////////////////////////////////

            #region Paths
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//California Housing//cal_housing_PSE.csv";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Ba de dados//WiscoinBreastCancer//wpbc_PSE.csv";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Ailerons//delta_ailerons_simulacao_pse.csv";
            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Furnas//furnas_pse.csv";
            #endregion


            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Diabetes//diabetes_simulacao_pse.csv";
            //SimulationResultPath = "c://Users//Edgar almeida//Desktop//Base de dados//Wiscoin Breast Cancer//wiscoin_breast_cancer_pse.csv";

            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados///Itaipu//itaipu_pse.csv";

            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Elevators//delta_elevators_pse.csv";

            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Auto-price//price_pse.csv";

            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Abalone-n//abalone_pse.csv";

            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Ailerons//delta_ailerons_pse.csv";

            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Elevators//delta_elevators_pse.csv";

            SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Caxias//caxias_pse.csv";

            //SimulationResultPath = "c://users//edgar almeida//desktop//base de dados//Wiscoin Breast Cancer//r_wpbc_pse.csv";

            //SimulationResultPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\ComputerActivity\cpu_act_pse.csv";

            //SimulationResultPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\Machine-Cpu\machine_pse.csv";

            //SimulationResultPath = @"C:\Users\Edgar Almeida\Desktop\Base de dados\triazines\triazines_pse.csv";

            //SimulationResultPath = "C://Users//Edgar Almeida//Desktop//Base de dados//Furnas//furnas_pse.csv";

            EvaluationFunction = EEvaluationFunctionType.PSE;

            File = new System.IO.StreamWriter(SimulationResultPath, true);

            info = "Seed; Hiden Nodes number; Selected Inputs; ";

            if (DataType == EDataType.Predction)
                info = string.Concat(info, (PerformanceInfo & EPerformanceInfo.EMQ) != EPerformanceInfo.None ? "EMQ-Train; EMQ-Val; " : string.Empty,
                    (PerformanceInfo & EPerformanceInfo.DEV) != EPerformanceInfo.None ? "DEV-Train; DEV-Val; " : string.Empty,
                    (PerformanceInfo & EPerformanceInfo.RMSE) != EPerformanceInfo.None ? "RMSE-Train; RMSE-Val; " : string.Empty,
                    (PerformanceInfo & EPerformanceInfo.EPMA) != EPerformanceInfo.None ? "EPMA-Train; EPMA-Val; " : string.Empty);
            else if ((PerformanceInfo & EPerformanceInfo.SR) != EPerformanceInfo.None)
                info = string.Concat("SuccessRate-Train; SuccessRate-Val; ");

            info = string.Concat(info, "Time");
            info = string.Concat(info, "; StopCount");

            File.WriteLine(info);
            File.Close();
            
            Console.WriteLine("Processando arquivo: " + SimulationResultPath);

            for (int i = 1; i <= Iterations; i++)
            {
                Console.WriteLine("Início iteração: " + i);
                int seed = Rand.Next();
                Random _Random = new Random(seed);

                StartTime = DateTime.Now;

                DataProvider prov = new DataProvider(DataSetPath, DataType, _Random);

                if (DataType == EDataType.Predction) prov.NormalizeData(inputA, inputB, outputA, outputB);

                prov.ShuffleDataSet(seed);
                prov.SplitData();
               

                HS hs = new HS(seed, MemorySize, MaxHiddenNodes, MaxIterations, prov, ActivationFunction, EvaluationFunction, PerformanceInfo, DoInputSearch);
                object[] returnVal = hs.Run();
                int stoped = (int)returnVal[1];
                HSParticle bestParticle = (HSParticle)returnVal[0];

                EndTime = DateTime.Now;

                string resultString = seed + "; " +
                                      bestParticle.Config.HidenNodesNumber + "; " +
                                      InputsToString(bestParticle.GetSubListValuesFromIndex((int)prov.InputsN)) + "; ";

                if (DataType == EDataType.Predction)
                {
                    if ((PerformanceInfo & EPerformanceInfo.EMQ) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.EMQTrain.ToString("0.######") + "; ";
                        resultString += bestParticle.EMQValidation.ToString("0.######") + "; ";
                    }

                    if ((PerformanceInfo & EPerformanceInfo.DEV) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.DEVTrain.ToString("0.######") + "; ";
                        resultString += bestParticle.DEVValidation.ToString("0.######") + "; ";
                    }

                    if ((PerformanceInfo & EPerformanceInfo.RMSE) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.RMSETrain.ToString("0.######") + "; ";
                        resultString += bestParticle.RMSEValidation.ToString("0.######") + "; ";
                    }

                    if ((PerformanceInfo & EPerformanceInfo.EPMA) != EPerformanceInfo.None)
                    {
                        resultString += bestParticle.EPMATrain.ToString("0.######") + "; ";
                        resultString += bestParticle.EPMAValidation.ToString("0.######") + "; ";
                    }
                }
                else if ((PerformanceInfo & EPerformanceInfo.SR) != EPerformanceInfo.None)
                {
                    resultString += bestParticle.SRTrain.ToString("0.######") + "; ";
                    resultString += bestParticle.SRValidation.ToString("0.######") + "; ";
                }

                resultString += EndTime.Subtract(StartTime).ToReadableString();
                resultString += "; " + stoped;

                File = new System.IO.StreamWriter(SimulationResultPath, true);
                File.WriteLine(resultString);
                File.Close();

                ////////////////// Salvando predição ///////////////////////////////
                if (SavePrediciton)
                {
                    prov = new DataProvider(DataSetPath, DataType, Rand);
                    if (DataType == EDataType.Predction) prov.NormalizeData(inputA, inputB, outputA, outputB);

                    int inputNodesLengh = bestParticle.GetFlagCountFromSubListValues((int)prov.InputsN);

                    Data[] dataSet = new Data[prov.DataSetLines];
                    int index = 0;

                    for (int j = 0; j < prov.DataSetLines; j++)
                    {
                        index = 0;
                        dataSet[j] = new Data(new double[inputNodesLengh], new double[prov.OutputsN]);
                        dataSet[j].Output = prov.DataSet[j].Output;

                        for (int k = 0; k < prov.InputsN; k++)
                        {
                            if (bestParticle.Values[k] == 1)
                            {
                                dataSet[j].Input[index] = prov.DataSet[j].Input[k];
                                index++;
                            }
                        }
                    }

                    int hiddenNodes = bestParticle.GetHiddenNodes((int)prov.InputsN, MaxHiddenNodes);

                    DataProvider provForPrediction = new DataProvider(dataSet, prov.DataType);

                    index = 0;
                    for (int k = 0; k < prov.InputsN; k++)
                    {
                        if (bestParticle.Values[k] == 1)
                        {
                            provForPrediction.MaxValue[index] = prov.MaxValue[k];
                            provForPrediction.MinValue[index] = prov.MinValue[k];
                            index++;
                        }
                    }

                    for (int l = 0; l < prov.OutputsN; l++)
                    {
                        provForPrediction.MaxValue[index] = prov.MaxValue[l + prov.InputsN];
                        provForPrediction.MinValue[index] = prov.MinValue[l + prov.InputsN];
                        index++;
                    }

                    ELM elm = new ELM(provForPrediction, hiddenNodes, seed, ActivationFunction, bestParticle.ELM.GetB, bestParticle.ELM.GetW);

                    double real = 0;
                    double prediction = 0;
                    double Error = 0;

                    File = new System.IO.StreamWriter(SimulationResultPath + "predicao.csv" + i, true);

                    for (int m = 0; m < provForPrediction.DataSetLines; m++)
                    {
                        for (int j = 0; j < provForPrediction.OutputsN; j++)
                        {
                            real = provForPrediction.DeNormalizeData(provForPrediction.DataSet[m].Output[j], outputA, outputB, j + inputNodesLengh);
                            prediction = provForPrediction.DeNormalizeData(elm.GetT[m][j], outputA, outputB, j + inputNodesLengh);

                            Error = real - prediction;
                            File.WriteLine(real + ";" + prediction + ";" + Error);
                        }
                    }
                    File.Close();
                }
                //////////////////////////////////////
                Console.WriteLine("Fim iteração: " + i);
            }
            Console.WriteLine("Fim simulação");

            Console.ReadKey();
        }

        static string InputsToString(List<byte> values)
        {
            string a = "";
            values.ForEach(v => a += v);
            return a;
        }
    }
}
